package com.eistert.java.datastructure._10_Heap;

import com.eistert.java.datastructure._01_Array._01_Array;

/**
 * 二叉堆
 * 	           62
 *  	     /    \
 *  	    41    30
 *  	   /  \   /  \
 *  	  28  16  22  13
 *  	 /\   /
 *    19 17 15
 *
 *    用数组存储，层序表示
 *    0     1     2     3     4     5     6     7     8     9
 *    62   41    30    28     16   22    13    19    17     15
 *
 *    父节点和左右节点的关系
 *    parent(i) = (i-1)/2
 *
 *    left child (i) = 2*i + 1;
 *    right child(i) = 2*i+ 2
 *
 * @Description: implementation code of the max heap
 * @Author: ai
 * @create: 2023-04-05 07:22
 */
public class _10_MaxHeap<E extends Comparable<E>> {
    private _01_Array<E> data;

    public _10_MaxHeap(int capacity) {
        data = new _01_Array<>(capacity);
    }

    public _10_MaxHeap() {
        data = new _01_Array<>();
    }

    public _10_MaxHeap(E[] arr){
        data = new _01_Array<>(arr);
        if(arr.length != 1){
            for(int i = parent(arr.length - 1) ; i >= 0 ; i --)
                siftDown(i);
        }
    }

    // 返回堆中的元素个数
    public int size() {
        return data.getSize();
    }

    // 返回一个布尔值, 表示堆中是否为空
    public boolean isEmpty() {
        return data.isEmpty();
    }

    /**
     * 返回完全二叉树的数组表示中，一个索引所表示的元素的父亲节点的索引
     * parent(i) = (i-1)/2
     */
    private int parent(int index) {
        if (index == 0) {
            throw new IllegalArgumentException("index-0 doesn't have parent.");
        }

        return (index - 1) / 2;
    }

    /**
     * 返回完全二叉树的数组表示中，一个索引所表示的元素的左孩子节点的索引
     * <p>
     * left child (i) = 2*i + 1;
     *
     * @param index
     * @return
     */
    private int leftChild(int index) {
        return index * 2 + 1;
    }

    /**
     * 返回完全二叉树的数组表示中，一个索引所表示的元素的右孩子节点的索引
     * right child(i) = 2*i+ 2
     *
     * @param index
     * @return
     */
    private int rightChild(int index) {
        return index * 2 + 2;
    }

    private void siftUp(int k) {

        while (k > 0 && data.get(parent(k)).compareTo(data.get(k)) < 0) {
            data.swap(k, parent(k));
            k = parent(k);
        }
    }

    // 向堆中添加元素
    public void add(E e) {
        data.addLast(e);
        siftUp(data.getSize() - 1);
    }

    // 看堆中的最大元素
    public E findMax() {
        if (data.getSize() == 0)
            throw new IllegalArgumentException("Can not findMax when heap is empty.");
        return data.get(0);
    }

    // 取出堆中最大元素
    public E extractMax() {

        E ret = findMax();

        data.swap(0, data.getSize() - 1);
        data.removeLast();
        siftDown(0);

        return ret;
    }

    private void siftDown(int k) {

        while (leftChild(k) < data.getSize()) {
            int j = leftChild(k); // 在此轮循环中,data[k]和data[j]交换位置
            if (j + 1 < data.getSize() &&
                    data.get(j + 1).compareTo(data.get(j)) > 0)
                j++;
            // data[j] 是 leftChild 和 rightChild 中的最大值

            if (data.get(k).compareTo(data.get(j)) >= 0)
                break;

            data.swap(k, j);
            k = j;
        }
    }

    /**
     * 取出堆中的最大元素，并且替换成元素e
     *
     * @param e
     * @return
     */
    public E replace(E e) {

        E ret = findMax();
        data.set(0, e);
        siftDown(0);
        return ret;
    }

}
